package engine;

public class Edge {

		/* @invariant v1!=null;
		 * @invariant v2!=null;
		 * @invariant weight >= 0;
		 */
		private /*@ spec_public @*/ Vertex v1;
		private /*@ spec_public @*/ Vertex v2;
		private /*@ spec_public @*/ float weight;
		/**
		 * Edge Constructor
		 * @param vert1 the first Vertex this Edge connects to
		 * @param vert2 the second Vertex this Edge connects to
		 * @param w the weight of the Edge
		 */
		/*
		 * @ requires !vert1.equals(vert2);
		 * @ requires vert1 !=null;
		 * @ requires vert2 != null;
		 * @requires w >= 0;
		 * @ ensures v1 !=null;
		 * @ ensures v2 !=null;
		 * @ensures weight >= 0;
		 * @assignable v1, v2, weight;
		 */
		public Edge(Vertex vert1,Vertex vert2,float w)
		{
			if(vert1==null){System.out.println("Illegal Input, cannot have a null vertex");return;}
			if(vert2==null){System.out.println("Illegal Input, cannot have a null vertex");return;}
			if(w<0){System.out.println("Illegal Input, cannot have a negative edge weight");return;}
			if(vert1.equals(vert2)){System.out.println("Illegal Input, cannot create edge between same vertex");return;}
			this.v1 = vert1;
			this.v2 = vert2;
			weight = w;
			v1.addEdge(this);
			v2.addEdge(this);
		}
		
		/**
		 * intersectPoint returns the XY point of intersection between two edges
		 * @param e the other edge
		 * @return the XY vector containing the point of intersection, null if not in intersection
		 */
		/*
		 * @requires e!=null;
		 * @returns new Vector;
		 */
		public Vector intersectPoint(Edge e){
			float x1 = this.getV1().getPos().getX();
			float y1 = this.getV1().getPos().getY();
			
			float x2 = this.getV2().getPos().getX();
			float y2 = this.getV2().getPos().getY();
			
			float x3 = e.getV1().getPos().getX();
			float y3 = e.getV1().getPos().getY();
			
			float x4 = e.getV2().getPos().getX();
			float y4 = e.getV2().getPos().getY();
			
		    float dx = x2 - x1;
		    float dy = y2 - y1;
		    float da = x4 - x3;
		    float db = y4 - y3;

		    if(da * dy - db * dx == 0)
		    	return null;
		    
		    float s = (dx * (y3 - y1) + dy * (x1 - x3)) / (da * dy - db * dx);
		    float t = (da * (y1 - y3) + db * (x3 - x1)) / (db * dx - da * dy);
		    
		    if(s >= 0f && s <= 1f && t >= 0f && t <= 1f)
		    	return new Vector(x1 + t * dx, y1 + t * dy);

		    return null;
		    
			/*float detpXNum = Matrix2X2.det(Matrix2X2.det(x1, y1, x2, y2), Matrix2X2.det(x1, 1, x2, 1),
										   Matrix2X2.det(x3, y3, x4, y4), Matrix2X2.det(x3, 1, x4, 1));
			
			float detpYNum = Matrix2X2.det(Matrix2X2.det(x1, y1, x2, y2), Matrix2X2.det(y1, 1, y2, 1),
					   					   Matrix2X2.det(x3, y3, x4, y4), Matrix2X2.det(x3, 1, x4, 1));
			
			float deno = Matrix2X2.det(Matrix2X2.det(x1, 1, x2, 1), Matrix2X2.det(y1, 1, y2, 1),
					   				   Matrix2X2.det(x3, 1, x4, 1), Matrix2X2.det(y3, 1, y4, 1));
			
			// if the denominator is 0 then there is no intersection, so return null
			
			Vector inter = new Vector(detpXNum/deno, detpYNum/deno);
			
			if((inter.getX() > getMinX() && inter.getX() < getMaxX()) && (inter.getX() > e.getMinX() && inter.getX() < e.getMaxX()))
				if((inter.getY() > getMinY() && inter.getY() < getMaxY()) && (inter.getY() > e.getMinY() && inter.getY() < e.getMaxY())){
					return inter;
				}
			
			return null;*/
		    
		    
		}
		/**
		 * getNormal returns the vector normal to the edge
		 * @return the vector normal to the edge
		 */
		/*
		 * @requires this!=null;
		 * @return new Vector;
		 */
		public Vector getNormal(){
			Vector dir = Vector.sub(v1.getPos(), v2.getPos());
			return Vector.normalize(new Vector(-dir.getY(), dir.getX()));
		}
		
		/**
		 * setV1 sets the first vertex of the edge
		 * @param v1 the first vertex held by the edge
		 */
		/*
		 * @ requires v1!=null;
		 * @ assignable this.v1;
		 * @ ensures this.v1 == v1; 
		 */
		public void setV1(Vertex v1)
		{
			if(v1==null){System.out.println("Illegal Input, cannot have null vertex");return;}
			this.v1 = v1;
		}
		/**
		 * getV1 returns the first vertex held by the edge
		 * @return the edge's first vertex
		 */
		/*
		 * @ assignable \nothing;
		 * @ returns v1;
		 * @ensures \result == this.v1; 
		 */
		public Vertex getV1()
		{
			return v1;
		}
		/**
		 * setV2 sets the first vertex of the edge
		 * @param v2 the first vertex held by the edge
		 */
		/*
		 * @ requires v2!=null;
		 * @ assignable this.v2;
		 * @ ensures this.v2 == v2; 
		 */		
		public void setV2(Vertex v2)
		{
			if(v2==null){System.out.println("Illegal Input, cannot have null vertex");return;}
			this.v2 = v2;
		}
		/**
		 * getV2 returns the second vertex held by the edge
		 * @return the edge's second vertex
		 */
		/*
		 * @ assignable \nothing;
		 * @ returns v2;
		 * @ensures \result == this.v2;
		 */
		public Vertex getV2()
		{
			return v2;
		}
		/**
		 * getWeight returns the weight of the edge - strength of the bond
		 * @return the edge's weight
		 */
		/*
		 * @ assignable \nothing;
		 * @ returns weight;
		 * @ensures \result == this.weight;
		 */
		public float getWeight()
		{
			return weight;
		}
		/**
		 * setWeight sets the weight of the edge - strength
		 * @param w the weight the edge will have after execution
		 */
		/*
		 * @ assignable weight
		 * @requires w >= 0;
		 * @ensures weight >= 0;
		 */
		public void setWeight(float w)
		{
			if(w<0){System.out.println("Illegal Input, negative edge weight");return;}
			weight = w;	
		}
		
		/***
		 * Returns the smallest X in out of both endpoints of the edge.
		 * @return Smallest x
		 */
		/*
		 * @requires this!=null;
		 * @returns float;
		 */
        private float getMinX()
        {
            if (v1.getPos().getX() < v2.getPos().getX())
            {
                return v1.getPos().getX();
            }
            else
                return v2.getPos().getX();
        }
		/***
		 * Returns the largest X in out of both endpoints of the edge.
		 * @return Largest x
		 */
        /*
         * @requires this!=null;
         * @returns float;
         */
        private float getMaxX()
        {
            if (v1.getPos().getX() > v2.getPos().getX())
            {
                return v1.getPos().getX();
            }
            else
                return v2.getPos().getX();
        }
		/***
		 * Returns the smallest Y in out of both endpoints of the edge.
		 * @return Smallest y
		 */
        /*
         * @requires this!=null;
         * @returns float;
         */
        private float getMinY()
        {
            if (v1.getPos().getY() < v2.getPos().getY())
            {
                return v1.getPos().getY();
            }
            else
                return v2.getPos().getY();
        }
		/***
		 * Returns the largest Y in out of both endpoints of the edge.
		 * @return Largest y
		 */
        /*
         * @requires this!=null;
         * @returns float;
         */
        private float getMaxY()
        {
            if (v1.getPos().getY() > v2.getPos().getY())
            {
                return v1.getPos().getY();
            }
            else
                return v2.getPos().getY();
        }
		/**
		 * getSlope returns the slope between the two verticies of the edge
		 * @return the slope between the verticies
		 */
        /*
         * @requires this!=null;
         * @returns float;
         */
		public float getSlope(){
			float rise = v2.getPos().getY() - v1.getPos().getY();
			float run  = v2.getPos().getX() - v1.getPos().getX();
			
			// if the run is zero, offset it a little bit
			// so we don't div by zero and kill the program
			run += run == 0 ? 0.01f : 0;
			
			return rise / run;
		}
		/**
		 * xWithinBounds tests if a passed x value is between the x values of the edge's verticies
		 * @param x the x value in question
		 * @return true if the x value is between the two verticies, false if not
		 */
		/*
		 *@requires this!=null;
		 *@return boolean; 
		 */
		public boolean xWithinBounds(float x){
			if(x>=getMinX() && x <= getMaxX())
				return true;
			else
				return false;
		}
		
		/**
		 * Returns a String representation of the edge
		 */
		public String toString()
		{
			return "Edge between "+v1+" and "+v2+" with weight "+weight+"\n";
		}
}
